// -----------------------------
// Author: 		Dhaenens Gert 
// Creation: 	8/8/2013 11:18:13 PM				
// -----------------------------

#ifndef _LuaClass_h
#define _LuaClass_h

#include "LuaObject.h"
#include "LuaRef.h"
#include "LuaStackGuard.h"
#include "Lua.h"
#include "LuaStack.h"
#include "LuaDataList.h"
#include "LuaStaticDataList.h"

#define MAX_CLASS_NAME_LENGTH	512

namespace ObjectLua
{

	/*
	//	Register C++ Class to Lua
	*/
	template<typename Class>
	class LuaClass : public LuaObject
	{
	public:
		LuaClass(Lua* lpLua);
		virtual ~LuaClass();

		/*
		//	bind the Class to Lua
		*/
		static LuaClass<Class> bind(Lua* lpLua, const char* name);

		/* 
		//	Inherit from another Class
		*/
		template<typename ParentClass>
		void inheritFrom();
		/*
		// Add a destructor to the Class
		*/
		void addDestructor();
		/*
		//	Add a constructor to the Class
		//	0 arguments
		*/
		void addConstructor();
		/*
		//	Add a constructor to the Class
		//	1 arguments
		*/
		template<typename P0>
		void addConstructor();
		/*
		//	Add a constructor to the Class
		//	2 arguments
		*/
		template<typename P0, typename P1>
		void addConstructor();
		/*
		//	Add a constructor to the Class
		//	3 arguments
		*/
		template<typename P0, typename P1, typename P2>
		void addConstructor();
		/*
		//	Add a constructor to the Class
		//	4 arguments
		*/
		template<typename P0, typename P1, typename P2, typename P3>
		void addConstructor();
		/*
		//	Add a constructor to the Class
		//	5 arguments
		*/
		template<typename P0, typename P1, typename P2, typename P3, typename P4>
		void addConstructor();
		/*
		//	Add a constructor to the Class
		//	6 arguments
		*/
		template<typename P0, typename P1, typename P2, typename P3, typename P4, typename P5>
		void addConstructor();
		/*
		//	Add a constructor to the Class
		//	7 arguments
		*/
		template<typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
		void addConstructor();



		/*
		//	Add a ProxyConstructor to the Class
		//	0 arguments
		*/
		void addProxyConstructor(const char* name);
		/*
		//	Add a ProxyConstructor to the Class
		//	1 arguments
		*/
		template<typename P0>
		void addProxyConstructor(const char* name);
		/*
		//	Add a ProxyConstructor to the Class
		//	2 arguments
		*/
		template<typename P0, typename P1>
		void addProxyConstructor(const char* name);
		/*
		//	Add a ProxyConstructor to the Class
		//	3 arguments
		*/
		template<typename P0, typename P1, typename P2>
		void addProxyConstructor(const char* name);
		/*
		//	Add a ProxyConstructor to the Class
		//	4 arguments
		*/
		template<typename P0, typename P1, typename P2, typename P3>
		void addProxyConstructor(const char* name);
		/*
		//	Add a ProxyConstructor to the Class
		//	5 arguments
		*/
		template<typename P0, typename P1, typename P2, typename P3, typename P4>
		void addProxyConstructor(const char* name);
		/*
		//	Add a ProxyConstructor to the Class
		//	6 arguments
		*/
		template<typename P0, typename P1, typename P2, typename P3, typename P4, typename P5>
		void addProxyConstructor(const char* name);
		/*
		//	Add a ProxyConstructor to the Class
		//	7 arguments
		*/
		template<typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
		void addProxyConstructor(const char* name);



		/*
		//	Add a method to the Class
		//	1 Return, 0 Arguments
		*/
		template<typename R>
		void addMethod(const char* name, R (Class::*lpMethod)());
		/*
		//	Add a method to the Class
		//	1 Return, 1 Arguments
		*/
		template<typename R, typename P0>
		void addMethod(const char* name, R (Class::*lpMethod)(P0));
		/*
		//	Add a method to the Class
		//	1 Return, 2 Arguments
		*/
		template<typename R, typename P0, typename P1>
		void addMethod(const char* name, R (Class::*lpMethod)(P0,P1));
		/*
		//	Add a method to the Class
		//	1 Return, 3 Arguments
		*/
		template<typename R, typename P0, typename P1, typename P2>
		void addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2));
		/*
		//	Add a method to the Class
		//	1 Return, 4 Arguments
		*/
		template<typename R, typename P0, typename P1, typename P2, typename P3>
		void addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2,P3));
		/*
		//	Add a method to the Class
		//	1 Return, 5 Arguments
		*/
		template<typename R, typename P0, typename P1, typename P2, typename P3, typename P4>
		void addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2,P3,P4));
		/*
		//	Add a method to the Class
		//	1 Return, 6 Arguments
		*/
		template<typename R, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5>
		void addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2,P3,P4,P5));
		/*
		//	Add a method to the Class
		//	1 Return, 7 Arguments
		*/
		template<typename R, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
		void addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2,P3,P4,P5,P6));



		/*
		//	Add a const method to the Class
		//	1 Return, 0 Arguments
		*/
		template<typename R>
		void addMethod(const char* name, R (Class::*lpMethod)() const);
		/*
		//	Add a const method to the Class
		//	1 Return, 1 Arguments
		*/
		template<typename R, typename P0>
		void addMethod(const char* name, R (Class::*lpMethod)(P0) const);
		/*
		//	Add a const method to the Class
		//	1 Return, 2 Arguments
		*/
		template<typename R, typename P0, typename P1>
		void addMethod(const char* name, R (Class::*lpMethod)(P0,P1) const);
		/*
		//	Add a const method to the Class
		//	1 Return, 3 Arguments
		*/
		template<typename R, typename P0, typename P1, typename P2>
		void addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2) const);
		/*
		//	Add a const method to the Class
		//	1 Return, 4 Arguments
		*/
		template<typename R, typename P0, typename P1, typename P2, typename P3>
		void addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2,P3) const);
		/*
		//	Add a const method to the Class
		//	1 Return, 5 Arguments
		*/
		template<typename R, typename P0, typename P1, typename P2, typename P3, typename P4>
		void addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2,P3,P4) const);
		/*
		//	Add a const method to the Class
		//	1 Return, 6 Arguments
		*/
		template<typename R, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5>
		void addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2,P3,P4,P5) const);
		/*
		//	Add a const method to the Class
		//	1 Return, 7 Arguments
		*/
		template<typename R, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
		void addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2,P3,P4,P5,P6) const);



		/*
		//	Add a Static Method to the Class
		//	1 Return, 0 Arguments
		*/
		template<typename R>
		void addStaticMethod(const char* name, R (*lpMethod)());
		/*
		//	Add a Static Method to the Class
		//	1 Return, 1 Arguments
		*/
		template<typename R, typename P0>
		void addStaticMethod(const char* name, R (*lpMethod)(P0));
		/*
		//	Add a Static Method to the Class
		//	1 Return, 2 Arguments
		*/
		template<typename R, typename P0, typename P1>
		void addStaticMethod(const char* name, R (*lpMethod)(P0,P1));
		/*
		//	Add a Static Method to the Class
		//	1 Return, 3 Arguments
		*/
		template<typename R, typename P0, typename P1, typename P2>
		void addStaticMethod(const char* name, R (*lpMethod)(P0,P1,P2));
		/*
		//	Add a Static Method to the Class
		//	1 Return, 4 Arguments
		*/
		template<typename R, typename P0, typename P1, typename P2, typename P3>
		void addStaticMethod(const char* name, R (*lpMethod)(P0,P1,P2,P3));
		/*
		//	Add a Static Method to the Class
		//	1 Return, 5 Arguments
		*/
		template<typename R, typename P0, typename P1, typename P2, typename P3, typename P4>
		void addStaticMethod(const char* name, R (*lpMethod)(P0,P1,P2,P3,P4));
		/*
		//	Add a Static Method to the Class
		//	1 Return, 6 Arguments
		*/
		template<typename R, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5>
		void addStaticMethod(const char* name, R (*lpMethod)(P0,P1,P2,P3,P4,P5));
		/*
		//	Add a Static Method to the Class
		//	1 Return, 7 Arguments
		*/
		template<typename R, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
		void addStaticMethod(const char* name, R (*lpMethod)(P0,P1,P2,P3,P4,P5,P6));


		/*
		//	Add a datamember to the Class
		//	@ name : name of the datamember
		//	@ Datamember : pointer to the datamember
		//
		*/
		template<typename T>
		void addDatamember(const char* name, T Class::*Datamember, bool bReadOnly = false);
		/*
		//	Add a static datamember to the Class
		//	@ name : name of the datamember
		//	@ Datamember : pointer to the datamember
		//
		*/
		template<typename T>
		void addStaticDatamember(const char* name, T* Datamember, bool bReadOnly = false);

		/*
		//	Add Unary Minus Operator
		*/
		template<typename R>
		void addUnaryMinusOperator();
		/*
		//	Add Addition Operator
		*/
		template<typename R, typename OtherClass>
		void addAdditionOperator();
		/*
		//	Add Subtraction Operator
		*/
		template<typename R, typename OtherClass>
		void addSubtractionOperator();
		/*
		//	Add Multiplication Operator
		*/
		template<typename R, typename OtherClass>
		void addMultiplicationOperator();
		/*
		//	Add Division Operator
		*/
		template<typename R, typename OtherClass>
		void addDivisionOperator();
		/*
		//	Add Index Operator
		*/
		template<typename R, typename IndexType>
		void addIndexOperator();

	private:
		static char						s_ClassName[MAX_CLASS_NAME_LENGTH];
		static Lua*						s_lpLua;
		static LuaTable					s_MetaTable;
		static LuaDataList<Class>		s_Datamembers;
		static LuaStaticDataList<Class>	s_StaticDataMembers;

		static int proxy_index(lua_State* lpState);
		static int proxy_newindex(lua_State* lpState);

		static int createObject_0(lua_State* lpState);
		template<typename P0>
		static int createObject_1(lua_State* lpState);
		template<typename P0, typename P1>
		static int createObject_2(lua_State* lpState);
		template<typename P0, typename P1, typename P2>
		static int createObject_3(lua_State* lpState);
		template<typename P0, typename P1, typename P2, typename P3>
		static int createObject_4(lua_State* lpState);
		template<typename P0, typename P1, typename P2, typename P3, typename P4>
		static int createObject_5(lua_State* lpState);
		template<typename P0, typename P1, typename P2, typename P3, typename P4, typename P5>
		static int createObject_6(lua_State* lpState);
		template<typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
		static int createObject_7(lua_State* lpState);

		template<typename R>
		static int unaryMinus(lua_State* lpState);
		template<typename R, typename OtherClass>
		static int additionOperator(lua_State* lpState);
		template<typename R, typename OtherClass>
		static int subtractionOperator(lua_State* lpState);
		template<typename R, typename OtherClass>
		static int multiplicationOperator(lua_State* lpState);
		template<typename R, typename OtherClass>
		static int divisionOperator(lua_State* lpState);
		template<typename R, typename IndexType>
		static int indexOperator(lua_State* lpState);
		template<typename R, typename IndexType>
		static int newIndexOperator(lua_State* lpState);

		static int deleteObject(lua_State* lpState);
		static int forceDestroyObject(lua_State* lpState);
		static int keepObject(lua_State* lpState);
	};

}

#include "LuaClass.inl"
#endif